package com.project.easy.common.framework.auth.core;

import com.project.easy.common.framework.auth.config.AuthConfig;
import com.project.easy.common.framework.auth.core.exception.AuthInitFailException;
import com.project.easy.common.framework.auth.core.exception.BusinessUserPasswordIllegalException;
import com.project.easy.common.framework.auth.core.exception.UserLockException;
import com.project.easy.common.framework.auth.core.exception.UserNotExistException;
import com.project.easy.common.framework.auth.dto.TokenContext;
import  com.project.easy.common.util.collection.CollectionUtil;
import  com.project.easy.common.util.dto.CommonContextParam;
import  com.project.easy.common.util.dto.UserInfo;
import  com.project.easy.common.util.exception.business.ParamRequireException;
import  com.project.easy.common.util.exception.sys.SysInitException;
import  com.project.easy.common.util.string.StringUtil;
import com.project.easy.common.framework.auth.dto.ReqLoginDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.OrderComparator;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author GreatorMan.cool
 * @copyright: Copyright (c) 2020
 * @company: 扬起科技有限公司
 * @date 2021/11/1 21:15
 * @description: 用户登录接口
 */
public abstract class AbstractLoginProcessor implements ApplicationContextAware, InitializingBean, LoginProcessor {
    /**
     * 日志
     */
    protected static final Logger LOG = LoggerFactory.getLogger(AbstractLoginProcessor.class);

    @Autowired(required = false)
    private AuthConfig authConfig;

    @Autowired(required = false)
    private  IPassword password;
    /**
     * 用户查询接口
     */
    protected IQueryUser queryUser;
    /**
     * 用户
     */
    private IToken iToken;
    /**
     * 权限查询
     */
    protected IQueryAuth queryAuth;

    /**
     * TOKEN 存储
     */
    @Autowired
    private ITokenStore tokenStore;
    /**
     * spring 上下文
     */
    protected ApplicationContext applicationContext;
    /**
     * 用户登录
     * @param reqLoginDto 登录信息
     * @return 返回登录信息
     */
    @Override
    public TokenContext login(ReqLoginDto reqLoginDto) {
        if (StringUtil.isBlank(reqLoginDto.getAcc()) || StringUtil.isBlank(reqLoginDto.getPassword())) {
            throw new ParamRequireException("用户名与密码不能为空!");
        }
        UserInfo userInfo = this.queryUser.userInfo(reqLoginDto.getAcc());
        if (Objects.isNull(userInfo)) {
            throw  new UserNotExistException("用户不存在");
        }
        if (Objects.isNull(userInfo.getStates()) || !userInfo.getStates()) {
            throw  new UserLockException("用户被锁, 请联系管理员");
        }
        if (!password.validata(userInfo.getPassword(), reqLoginDto.getPassword(), "")) {
            throw  new BusinessUserPasswordIllegalException("用户密码不正确");
        }
        //如果不为空 调用权限存储
        if (!Objects.isNull(this.queryAuth)) {
            //加载权限数据
            this.queryAuth.load(userInfo);
        }
        //清空密码
        userInfo.setPassword(null);
        //开始生成TOKEN数据
        TokenContext tokenContext = new TokenContext(userInfo);
        this.iToken.createToken(tokenContext);
        //存储TOKEN
        this.tokenStore.saveToken(tokenContext);
        return tokenContext;
    }
    /**
     * @param generatorDto 退出
     * @return true 登录成功 false 退出登录失败
     */
    @Override
    public  Boolean  loginOut(CommonContextParam generatorDto) {
       return this.tokenStore.remove(generatorDto.getPubHeader().getToken());
    }
    /**
     * Set the ApplicationContext that this object runs in.
     * Normally this call will be used to initialize the object.
     * <p>Invoked after population of normal bean properties but before an init callback such
     * as {@link InitializingBean#afterPropertiesSet()}
     * or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
     * {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
     * {@link MessageSourceAware}, if applicable.
     *
     * @param applicationContext the ApplicationContext object to be used by this object
     * @throws BeansException in case of context initialization errors
     * @throws BeansException              if thrown by application context methods
     * @see BeanInitializationException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    /**
     * Invoked by the containing {@code BeanFactory} after it has set all bean properties
     * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
     * <p>This method allows the bean instance to perform validation of its overall
     * configuration and final initialization when all bean properties have been set.
     *
     * @throws Exception in the listener of misconfiguration (such as failure to set an
     *                   essential property) or if initialization fails for any other reason
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        if (LOG.isDebugEnabled()) {
            LOG.debug("登录初始化开始");
        }
        /**
         * 初始化IQueryUser 用户查询
         */
        Map<String, IQueryUser> queryUserMap = this.applicationContext.getBeansOfType(IQueryUser.class);
        if (CollectionUtil.isNull(queryUserMap)) {
            throw  new SysInitException("系统登录配置异常,最少有一个用户查询");
        }
        List<IQueryUser> queryUsers = queryUserMap.values().stream().collect(Collectors.toList());
        OrderComparator.sort(queryUsers);
        this.queryUser = queryUsers.get(0);

        Map<String, IQueryAuth> queryAuthMap = this.applicationContext.getBeansOfType(IQueryAuth.class);
        if (!CollectionUtil.isNull(queryAuthMap)) {
            List<IQueryAuth> queryAuthList = queryAuthMap.values().stream().collect(Collectors.toList());
            OrderComparator.sort(queryAuthList);
            this.queryAuth = queryAuthList.get(0);
        }
        Map<String, IToken> iTokenMap = this.applicationContext.getBeansOfType(IToken.class);
        List<IToken> tokens = iTokenMap.values().stream().collect(Collectors.toList());
        OrderComparator.sort(tokens);
        this.iToken = tokens.get(0);
        if (LOG.isDebugEnabled()) {
            LOG.debug("登录初始化开始");
        }
        //如果登陆必须要
        if (Objects.isNull(password)
                || Objects.isNull(this.queryUser)
                || Objects.isNull(this.iToken) || Objects.isNull(this.authConfig)) {
            throw  new AuthInitFailException("登陆初始化失败");
        }
    }
    /**
     * 用户登陆成功后回调
     * @param userInfo 用户信息
     * @return 返回true 成功 false 失败
     */
    public abstract Boolean loginEx(UserInfo userInfo);
    /**
     * @param commonContextParam 参数
     * @return 返回true 成功 false 失败
     */
    public  Boolean loginOutEx(CommonContextParam commonContextParam) {
        return  Boolean.TRUE;
    }
}
